/*
 * Copyright 2009 SHOP.COM
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.shop.opensocial.mediated.shindig.config;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.inject.AbstractModule;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import com.google.inject.TypeLiteral;
import com.google.inject.name.Names;
import com.shop.opensocial.mediated.config.MediatedDriverFactory;
import com.shop.opensocial.mediated.shindig.request.WrappedRenderer;
import com.shop.opensocial.mediated.shindig.spi.MediatedOpenSocialService;
import com.shop.opensocial.mediated.shindig.util.ContainerScriptsAndConstantsMediatorImplementation;
import com.shop.opensocial.mediated.shindig.util.OAuthValidatorImplementation;
import org.apache.shindig.auth.AnonymousAuthenticationHandler;
import org.apache.shindig.auth.AuthenticationHandler;
import org.apache.shindig.common.crypto.BasicBlobCrypter;
import org.apache.shindig.common.crypto.BlobCrypter;
import org.apache.shindig.common.servlet.ParameterFetcher;
import org.apache.shindig.gadgets.oauth.OAuthFetcherConfig;
import org.apache.shindig.gadgets.oauth.OAuthModule;
import org.apache.shindig.gadgets.oauth.OAuthRequest;
import org.apache.shindig.gadgets.oauth.OAuthStore;
import org.apache.shindig.gadgets.render.Renderer;
import org.apache.shindig.protocol.DataServiceServletFetcher;
import org.apache.shindig.protocol.DefaultHandlerRegistry;
import org.apache.shindig.protocol.HandlerRegistry;
import org.apache.shindig.protocol.conversion.BeanConverter;
import org.apache.shindig.protocol.conversion.BeanJsonConverter;
import org.apache.shindig.protocol.conversion.BeanXStreamConverter;
import org.apache.shindig.protocol.conversion.xstream.XStreamConfiguration;
import org.apache.shindig.social.core.util.BeanXStreamAtomConverter;
import org.apache.shindig.social.core.util.xstream.XStream081Configuration;
import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
import org.apache.shindig.social.opensocial.service.ActivityHandler;
import org.apache.shindig.social.opensocial.service.AppDataHandler;
import org.apache.shindig.social.opensocial.service.PersonHandler;
import org.apache.shindig.social.opensocial.spi.ActivityService;
import org.apache.shindig.social.opensocial.spi.AppDataService;
import org.apache.shindig.social.opensocial.spi.PersonService;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
import java.util.Set;

/**
 * @author Jordan Zimmerman
 */
@SuppressWarnings({"deprecation"})
public class MediatedGuiceModule extends AbstractModule
{
	@Override
	protected void configure()
	{
		bind(Boolean.class).annotatedWith(Names.named(AnonymousAuthenticationHandler.ALLOW_UNAUTHENTICATED)).toInstance(Boolean.TRUE);

		bind(HandlerRegistry.class).to(DefaultHandlerRegistry.class);

		bind(ParameterFetcher.class).annotatedWith(Names.named("DataServiceServlet")).to(DataServiceServletFetcher.class);

		bind(XStreamConfiguration.class).to(XStream081Configuration.class);
		bind(BeanConverter.class).annotatedWith(Names.named("shindig.bean.converter.xml")).to(BeanXStreamConverter.class);
		bind(BeanConverter.class).annotatedWith(Names.named("shindig.bean.converter.json")).to(BeanJsonConverter.class);
		bind(BeanConverter.class).annotatedWith(Names.named("shindig.bean.converter.atom")).to(BeanXStreamAtomConverter.class);

		bind(new TypeLiteral<List<AuthenticationHandler>>()
		{
		}).toProvider(MediatedAuthenticationHandlerProvider.class);

		bind(new TypeLiteral<Set<Object>>()
		{
		}).annotatedWith(Names.named("org.apache.shindig.protocol.handlers")).toInstance(ImmutableSet.<Object>of(ActivityHandler.class, AppDataHandler.class, PersonHandler.class));

		bind(PersonService.class).to(MediatedOpenSocialService.class);
		bind(AppDataService.class).to(MediatedOpenSocialService.class);
		bind(ActivityService.class).to(MediatedOpenSocialService.class);

		bind(BlobCrypter.class).annotatedWith(Names.named(OAuthFetcherConfig.OAUTH_STATE_CRYPTER)).toProvider(MediatedOAuthCrypterProvider.class);
		bind(OAuthStore.class).toProvider(MediatedOAuthStoreProvider.class);
		bind(OAuthRequest.class).toProvider(OAuthModule.OAuthRequestProvider.class);
		bind(OAuthDataStore.class).to(MediatedOpenSocialService.class);

		bind(Renderer.class).to(WrappedRenderer.class);

		MediatedDriverFactory.setValidator(OAuthValidatorImplementation.instance);
		MediatedDriverFactory.setScripts(ContainerScriptsAndConstantsMediatorImplementation.instance);
	}

	@Singleton
	public static class MediatedOAuthCrypterProvider implements Provider<BlobCrypter>
	{
		private BasicBlobCrypter fCrypter;

		@Inject
		public MediatedOAuthCrypterProvider() throws IOException
		{
			fCrypter = null;
		}

		@Override
		public synchronized BlobCrypter get()
		{
			if ( fCrypter == null )
			{
				@SuppressWarnings({"deprecation"})
				byte[] key = MediatedDriverFactory.getDriver().getAuthenticationHandlerMediator().getAuthenticationKey();
				fCrypter = new BasicBlobCrypter(key);
			}
			return fCrypter;
		}
	}

	private static class MediatedAuthenticationHandlerProvider implements Provider<List<AuthenticationHandler>>
	{
		@Inject
		public MediatedAuthenticationHandlerProvider(MediatedOpenSocialService service)
		{
			fHandlers = Collections.unmodifiableList(Lists.newArrayList((AuthenticationHandler)service));
		}

		@Override
		public List<AuthenticationHandler> get()
		{
			return fHandlers;
		}

		private final List<AuthenticationHandler> fHandlers;
	}

	private static class MediatedOAuthStoreProvider implements Provider<OAuthStore>
	{
		@Inject
		public MediatedOAuthStoreProvider(MediatedOpenSocialService service)
		{
			fService = service;
		}

		@Override
		public OAuthStore get()
		{
			return fService;
		}

		private final MediatedOpenSocialService fService;
	}
}

